package org.android.bbdroid.webservice;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;

import org.android.bbdroid.webservice.data.CategoryForum;
import org.android.bbdroid.webservice.data.Forum;
import org.android.bbdroid.webservice.data.Post;
import org.android.bbdroid.webservice.data.Topic;
import org.android.bbdroid.webservice.data.TopicForum;
import org.android.bbdroid.webservice.data.TopicInfo;
import org.android.bbdroid.webservice.data.User;

import android.util.Log;
import de.timroes.axmlrpc.XMLRPCClient;
import de.timroes.axmlrpc.XMLRPCException;

public final class PHPBBClient {
	private static PHPBBClient instance = null;
	private XMLRPCClient client;
	private User currentLoggedUser = null;
	private URL endpoint;
	private SiteInfo info;

	private PHPBBClient(String url) throws MalformedURLException,
			XMLRPCException {
		this.endpoint = new URL(new URL(url),"mo/mo.php");
		this.client = new XMLRPCClient(endpoint);
		this.info = new SiteInfo((HashMap) index().get("config"));
	}

	private HashMap _login(String username, String password)
			throws XMLRPCException {
		Object[] arrayOfObject = new Object[8];
		arrayOfObject[0] = "";
		arrayOfObject[1] = "";
		arrayOfObject[2] = username;
		arrayOfObject[3] = password;
		arrayOfObject[4] = false;
		arrayOfObject[5] = 0;
		arrayOfObject[6] = true;
		arrayOfObject[7] = "";
		return (HashMap) this.client.call("login", arrayOfObject);
	}

	private HashMap _logout() throws XMLRPCException {
		Object[] arrayOfObject = new Object[3];
		arrayOfObject[0] = "";
		arrayOfObject[1] = "";
		arrayOfObject[2] = currentLoggedUser.getSid();
		return (HashMap) this.client.call("logout", arrayOfObject);
	}

	private HashMap _postReply(TopicInfo info, String subject, String message)
			throws XMLRPCException {
		Object[] arrayOfObject = new Object[11];
		arrayOfObject[0] = "";
		arrayOfObject[1] = "";
		arrayOfObject[2] = info.getId();
		arrayOfObject[3] = info.getForumId();
		arrayOfObject[4] = "";
		arrayOfObject[5] = "";
		arrayOfObject[6] = subject;
		arrayOfObject[7] = message;
		arrayOfObject[8] = 0;
		arrayOfObject[9] = true;
		arrayOfObject[10] = currentLoggedUser == null ? "" : currentLoggedUser
				.getSid();

		return (HashMap) this.client.call("postreply", arrayOfObject);

	}

	public static PHPBBClient createInstance(String paramString) {
		try {
			instance = new PHPBBClient(paramString);
			return instance;
		} catch (Exception e) {
			return null;
		}
	}

	private void fillTopics(TopicForum forum, Object[] topicsData) {
		for (int i = 0; i < topicsData.length; i++) {
			HashMap topicData = (HashMap) topicsData[i];
			String title = (String) topicData.get("topic_title");
			int topicId = (Integer) topicData.get("topic_id");
			int forumId = (Integer) topicData.get("forum_id");
			int topicType = (Integer) topicData.get("topic_type");
			int topicStatus = (Integer) topicData.get("topic_status");
			int topicReplies = (Integer) topicData.get("topic_replies");

			forum.addTopic(new TopicInfo(topicId, forumId, title, topicType,
					topicStatus, topicReplies));
		}
	}

	private List<Forum> getForums(Object[] forumsData)
  {
		List<Forum> forums=new LinkedList<Forum>();
		try{
			for(Object forum: forumsData){
				HashMap forumData=(HashMap)forum;
				int forumType=(Integer)forumData.get("forum_type");
				if(forumType==0){ // category forum
					CategoryForum cf=new CategoryForum((Integer)forumData.get("forum_id"),
													   (String)forumData.get("forum_name"),
													   (String)forumData.get("forum_desc"));
					Object[] subforums = (Object[])viewforum(cf, 0).get("forum_rows");
					
					for(Object subforum: subforums){
						HashMap subforumData=(HashMap)subforum;
						int subforumType=(Integer)subforumData.get("forum_type");
						if(subforumType==0){ //category
							cf.addForum(new CategoryForum((Integer)subforumData.get("forum_id"),
														  (String)subforumData.get("forum_name"),
														  (String)subforumData.get("forum_desc")));
						}else{
					        cf.addForum(new TopicForum((Integer)subforumData.get("forum_id"),
					        						   (String)subforumData.get("forum_name"),
					        						   (String)subforumData.get("forum_desc"),
					        						   (Boolean)subforumData.get("is_auth_post")));
						}
					}
					forums.add(cf);
				}else if(forumType==1){ //topic forum
		            forums.add(new TopicForum(
		            		((Integer)forumData.get("forum_id")), 
		            		(String)forumData.get("forum_name"),
		            		(String)forumData.get("forum_desc"),
		            		((Boolean)forumData.get("is_auth_post"))));
				}
			}
		}catch(XMLRPCException e){
			return null;
		}
    return forums;
  }

	public static PHPBBClient getInstance() {
		return instance;
	}

	private HashMap index() throws XMLRPCException {
		Object[] arrayOfObject = new Object[1];
		arrayOfObject[0] = "";
		return (HashMap) this.client.call("index", arrayOfObject);
	}

	private HashMap newtopic(TopicForum forum, String subject, String message)
			throws XMLRPCException {
		Object[] arrayOfObject = new Object[12];
		arrayOfObject[0] = "";
		arrayOfObject[1] = "";
		arrayOfObject[2] = forum.getId();
		arrayOfObject[3] = "";
		arrayOfObject[4] = "";
		arrayOfObject[5] = subject;
		arrayOfObject[6] = message;
		arrayOfObject[7] = 0;
		arrayOfObject[8] = 0;
		arrayOfObject[9] = 0;
		arrayOfObject[10] = true;
		arrayOfObject[11] = currentLoggedUser == null ? "" : currentLoggedUser
				.getSid();
		return (HashMap) this.client.call("newtopic", arrayOfObject);

	}

	private HashMap viewforum(int id, int start) throws XMLRPCException {
		Object[] arrayOfObject = new Object[9];
		arrayOfObject[0] = "";
		arrayOfObject[1] = id;
		arrayOfObject[2] = "";
		arrayOfObject[3] = start;
		arrayOfObject[4] = currentLoggedUser == null ? "" : currentLoggedUser
				.getSid();
		arrayOfObject[5] = "";
		arrayOfObject[6] = "";
		arrayOfObject[7] = 0;
		arrayOfObject[8] = "";

		return (HashMap) this.client.call("viewforum", arrayOfObject);
	}

	private HashMap viewforum(Forum forum, int start) throws XMLRPCException {
		return viewforum(forum.getId(), start);
	}

	private HashMap viewpost(Post post, int start) throws XMLRPCException {
		Object[] arrayOfObject = new Object[14];
		arrayOfObject[0] = "";
		arrayOfObject[1] = "";
		arrayOfObject[2] = post.getId();
		arrayOfObject[3] = post.getTopicId();
		arrayOfObject[4] = post.getForumId();
		arrayOfObject[5] = "";
		arrayOfObject[6] = start;
		arrayOfObject[7] = 0;
		arrayOfObject[8] = currentLoggedUser == null ? "" : currentLoggedUser
				.getSid();
		arrayOfObject[9] = "";
		arrayOfObject[10] = "";
		arrayOfObject[11] = 0;
		arrayOfObject[12] = 0;
		arrayOfObject[13] = "";

		return (HashMap) this.client.call("viewpost", arrayOfObject);
	}

	private HashMap viewtopic(TopicInfo topic, int start)
			throws XMLRPCException {
		Object[] arrayOfObject = new Object[13];
		arrayOfObject[0] = "";
		arrayOfObject[1] = "";
		arrayOfObject[2] = topic.getId();
		arrayOfObject[3] = topic.getForumId();
		arrayOfObject[4] = "";
		arrayOfObject[5] = start;
		arrayOfObject[6] = 0;
		arrayOfObject[7] = currentLoggedUser == null ? "" : currentLoggedUser
				.getSid();
		arrayOfObject[8] = "";
		arrayOfObject[9] = "";
		arrayOfObject[10] = 0;
		arrayOfObject[11] = 0;
		arrayOfObject[12] = "";

		return (HashMap) this.client.call("viewtopic", arrayOfObject);
	}

	public boolean createTopic(TopicForum forum, String subject, String message)
  {
    try
    {
      return newtopic(forum, subject, message).get("is_auth_post").equals(true);
    }
    catch (XMLRPCException e)
    {
    	return false;
    }
  }

	public User getCurrentUser() {
		return this.currentLoggedUser;
	}

	public URL getEndpoint() {
		return this.endpoint;
	}

	public List<Forum> getForums()
  {
    try
    {
      return getForums((Object[])index().get("forum_rows"));
    }
    catch (XMLRPCException e)
    {
      return null;
    }
  }

	public List<Forum> getForums(Forum forum) {
		return getForums(forum, 0);
	}

	public List<Forum> getForums(Forum forum, int start) {
		try {
			HashMap data = viewforum(forum, start);
			List<Forum> forums = getForums((Object[]) data.get("forum_rows"));
			if ((forum instanceof TopicForum)) {
				Object[] topicsData = (Object[]) data.get("topic_rows");
				fillTopics((TopicForum) forum, topicsData);
			}
			return forums;
		} catch (XMLRPCException localXMLRPCException) {
			return null;
		}
	}

	public String getPostFullText(Post post) {
		try {
			String fullText = ((HashMap) viewpost(post, 0).get("post_data"))
					.get("post_text").toString();
			return fullText;
		} catch (XMLRPCException localXMLRPCException) {
			return null;
		}
	}

	public SiteInfo getSiteInfo() {
		return this.info;
	}

	public Topic getTopic(TopicInfo paramTopicInfo) {
		return getTopic(paramTopicInfo, 0);
	}

	public Topic getTopic(TopicInfo info, int start) {
		
		try{

		TopicInfo newInfo=getTopicInfo(info.getForumId(),info.getId());
		if(newInfo==null) return null;
		
		HashMap topicData=viewtopic(newInfo,start);
		
		
		Topic topic=new Topic(newInfo, (Boolean)topicData.get("is_auth_reply"));
		Object[] posts=(Object[]) topicData.get("post_rows");
		
		for(Object post: posts){
			HashMap postData=(HashMap)post;
			int postId = (Integer) postData.get("post_id");
			int topicId = (Integer) postData.get("topic_id");
			String username = (String) postData.get("username");
			String subject = (String) postData.get("post_subject");
			String text = (String) postData.get("post_text");
			String time = (String) postData.get("post_time");
			Post p = new Post(postId, topicId, info.getForumId(),
					username, subject, text, time);
			topic.addPost(p);
		}
		return topic;
		}catch(XMLRPCException e){
			return null;
		}
	}

	public TopicInfo getTopicInfo(int forumId, int topicId) {
		
		try{
			HashMap forumData=viewforum(forumId,0);
			Object[] topics = (Object[]) forumData.get("topic_rows");
			for(Object topic:topics){
				HashMap topicData=(HashMap)topic;
				int tid=(Integer)topicData.get("topic_id");
				if(tid==topicId){
					return new TopicInfo(tid,
							(Integer) topicData.get("forum_id"),
							(String) topicData.get("topic_title"),
							(Integer) topicData.get("topic_type"),
							(Integer) topicData.get("topic_status"),
							(Integer) topicData.get("topic_replies"));
				}
			}
		}catch(XMLRPCException e){
			return null;
		}
		return null;
	}

	public boolean isLogged() {
		return currentLoggedUser !=null && !currentLoggedUser.getSid().isEmpty();
	}

	public User login(String username, String password) {
		if(isLogged()) return currentLoggedUser;
		
		try{
			
			HashMap data=_login(username,password);
			HashMap loginResult=(HashMap)data.get("result");
			if(loginResult.get("status").equals(3)){
				String sid=(String)loginResult.get("session_id");
				currentLoggedUser=new User(username,sid);
				client=new XMLRPCClient(new URL(endpoint,"mo.php?sid="+sid));
			}else{
				Log.d("CONSOLE", "Error logging in");
				Log.d("CONSOLE", "Status=" + loginResult.get("status")
						+ " Message=" + loginResult.get("error_msg"));
				currentLoggedUser=null;
			}
		}catch(Exception e){
			return null;
		}
		return currentLoggedUser;
	}

	public boolean logout() {
		if(!isLogged()) return true;
		try{
			String res=(String)_logout().get("index_url");
			return !(res==null || res.isEmpty());
		}catch(XMLRPCException e){
			return false;
		}
		
	}

	public boolean postReply(TopicInfo topic, String subject, String message) {
		try {
			HashMap result = _postReply(topic, subject, message);
			return result.get("is_auth_reply").equals(true);
		} catch (XMLRPCException localXMLRPCException) {
			return false;
		}
	}

	public static class SiteInfo {
		private int postsPerPage;
		private String siteDescription;
		private String siteName;
		private int topicsPerPage;

		private SiteInfo(HashMap paramHashMap) {
			this.siteName = ((String) paramHashMap.get("sitename"));
			this.siteDescription = ((String) paramHashMap.get("site_desc"));
			this.topicsPerPage = (Integer) paramHashMap.get("topics_per_page");
			this.postsPerPage = (Integer) paramHashMap.get("posts_per_page");
		}

		public int getPostsPerPage() {
			return this.postsPerPage;
		}

		public String getSiteDescription() {
			return this.siteDescription;
		}

		public String getSiteName() {
			return this.siteName;
		}

		public int getTopicsPerPage() {
			return this.topicsPerPage;
		}

		public String toString() {
			TreeMap<String, String> map = new TreeMap<String, String>();
			map.put("site name", this.siteName);
			map.put("site desc", this.siteDescription);
			map.put("topics per page", "" + topicsPerPage);
			return map.toString();
		}
	}
}